package org.jaaksi.customviewdemo.view

import android.animation.ValueAnimator
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import android.view.animation.LinearInterpolator
import androidx.core.graphics.toColorInt
import androidx.core.view.doOnPreDraw
import org.jaaksi.customviewdemo.chart.DataPoint
import org.jaaksi.customviewdemo.chart.Scale
import org.jaaksi.customviewdemo.chart.toSmoothLinePath

/**
 * 1.升窄降宽
 * 2.不满一屏的动画
 * 3.满一屏之后的动画 todo 顿的比较明显
 * 4.绘制渐变
 * 5.保存数据与恢复
 * todo
 * 6.从onFrame如何提取波峰波谷（忽略掉小波峰波谷，根据波峰距波谷的个数）
 * 7.处理接近水平的情况，如果取到的波峰与上次波谷相差<最大波峰与波谷相差的1/n，进行矫正该波峰，波谷同理
 */
class WaveChart @JvmOverloads constructor(
    context: Context?,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    //画笔
    private var mPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG)
    private val fillPaint = Paint()
    private val linearGradient by lazy(LazyThreadSafetyMode.NONE) {
        LinearGradient(
            0f,
            0f,
            0f,
            height.toFloat(),
            intArrayOf("#4DFF4F69".toColorInt(), "#00FF4F69".toColorInt()),
            null,
            Shader.TileMode.MIRROR
        )
    }

    //折现的颜色
    private var mLineColor = Color.parseColor("#F7305A")

    //心电图折现
    private val mPath: Path = Path()
    private val fillPath = Path()
    private val data = mutableListOf<DataPoint>()


    // 波谷与波谷的距离，控件宽度/9
    private var peakWith = 0f

    // 上升波宽度
    private var riseWith = 0f

    // 下降波宽度
    private var declineWith = 0f

    private var scale: Scale = Scale(-1f, -1f)
    private var frame = RectF()
    private var animFrame = RectF()
    private var anim: ValueAnimator? = null
    private val animDuration = 400L

    // 一屏最多多少个波段
    var maxWaveBand = 9
    val maxPointSize: Int
        get() = maxWaveBand * 2 + 1

    companion object {
        const val MIN_POINT_SIZE = 3
    }

    init {
        //设置画笔style
        mPaint.style = Paint.Style.STROKE
        mPaint.isAntiAlias = true
        fillPaint.style = Paint.Style.FILL
        mPaint.color = mLineColor
        mPaint.strokeWidth = 5f
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        peakWith = (w / maxWaveBand).toFloat()
        riseWith = peakWith * 0.3f
        declineWith = peakWith - riseWith
        frame.set(0f, 0f, w.toFloat(), h.toFloat())
        animFrame.set(0f, 0f, w.toFloat(), h.toFloat())

    }

    // 要在setData之前调用
    fun setScale(scale: Scale) {
        this.scale = scale
    }

    /**
     * @param data 至少3条数据
     * @param withAnim 是否进行动画
     */
    fun setData(data: List<Float>, withAnim: Boolean = false) {
        this.data.clear()
        if (data.size >= MIN_POINT_SIZE) {
            this.data.addAll(data.map { DataPoint(it) })
            doOnPreDraw {
                initDataPoints()
                // 开始动画
                if (withAnim) {
                    startAnim()
                } else {
                    anim?.end()
                    anim = null
                }
            }
        }
        postInvalidate()
    }

    private fun startAnim() {
        anim?.end()
        anim = null
        when {
            data.size == MIN_POINT_SIZE -> {
                anim =
                    ValueAnimator.ofFloat(data.first().screenPositionX, data.last().screenPositionX)
                        .also {
                            it.duration = animDuration * 2
                        }
            }
            data.size in (MIN_POINT_SIZE + 1)..maxPointSize -> {
                anim = ValueAnimator.ofFloat(
                    data[data.lastIndex - 1].screenPositionX,
                    data.last().screenPositionX
                ).also {
                    it.duration = animDuration
                }
            }
            data.size > maxPointSize -> {
                // 超过一屏幕，移动画布
                anim = ValueAnimator.ofFloat(
                    data[data.lastIndex - 1].screenPositionX,
                    data.last().screenPositionX
                ).also {
                    it.duration = animDuration
                }

            }
        }
        anim?.apply {
            interpolator = LinearInterpolator()
            addUpdateListener {
                animFrame.right = it.animatedValue as Float
                postInvalidate()
            }
            start()
        }
    }


    private fun initDataPoints() {
        if (data.size < MIN_POINT_SIZE) return

        val scaleSize = scale.size

        // // 如果第一条数据为波谷则奇数为波谷，偶数为波峰，反之
        // todo 与中间波峰波谷边界的贝塞尔连接是断开的，
        var firstIsPeak = data.first().value > data[1].value
        data.forEachIndexed { index, dataPoint ->
            // 升窄降宽
            dataPoint.screenPositionX =
                frame.left + index / 2 * peakWith + (index % 2) * if (firstIsPeak) declineWith else riseWith
            dataPoint.screenPositionY =
                frame.bottom -
                        (height * (dataPoint.value - scale.min) / scaleSize)
        }
    }

    private fun initFillPath() {
        fillPath.reset()
        fillPath.addPath(mPath)
        fillPath.lineTo(data.last().screenPositionX, frame.bottom)
        fillPath.lineTo(data.first().screenPositionX, frame.bottom)
        fillPath.close()
    }


    override fun onDraw(canvas: Canvas) {
        if (data.size < MIN_POINT_SIZE) return
        data.toSmoothLinePath(mPath)
        initFillPath()
        when {
            // 不足一屏时，根据动画裁减画布
            animFrame.right <= frame.right -> {
                canvas.save()
                canvas.clipRect(animFrame)
                canvas.drawPath(mPath, mPaint)
                drawShader(canvas)
                canvas.restore()
            }
            else -> { // 超过一屏后，移动画布
                canvas.save()
                canvas.translate(-(animFrame.right - frame.right), 0f)
                canvas.drawPath(mPath, mPaint)
                drawShader(canvas)
                canvas.restore()
            }
        }

    }

    private fun drawShader(canvas: Canvas) {
        val id = canvas.save()
        canvas.clipPath(fillPath)
        fillPaint.shader = linearGradient
        canvas.drawPath(fillPath, fillPaint)
        canvas.restoreToCount(id)
    }

}